using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections;

namespace DotNetGear.WinInet
{
    class WinInet
    {
        private enum Error
        {
            Success = 0,
            FileNotfound = 2,
            AccessDenied = 5,
            InvalidParameter = 87,
            InsufficientBuffer = 122,
            NoMoreItems = 259,
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct FileTime
        {
            public UInt32 dwLowDateTime;
            public UInt32 dwHighDateTime;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct EntryInfo
        {
            public UInt32 dwStructSize;
            public string lpszSourceUrlName;
            public string lpszLocalFileName;
            public UInt32 CacheEntryType;
            public UInt32 dwUseCount;
            public UInt32 dwHitRate;
            public UInt32 dwSizeLow;
            public UInt32 dwSizeHigh;
            public FileTime LastModifiedTime;
            public FileTime ExpireTime;
            public FileTime LastAccessTime;
            public FileTime LastSyncTime;
            public IntPtr lpHeaderInfo;
            public UInt32 dwHeaderInfoSize;
            public string lpszFileExtension;
            public UInt32 dwExemptDelta;
        }

        private class EntryInfoWrapper : IDisposable
        {
            protected IntPtr _pointer = IntPtr.Zero;
            private UInt32 _prevSize = 0;
            protected bool _disposed = false;

            public UInt32 Size = 0;

            public IntPtr Pointer
            {
                get
                {
                    lock (this)
                    {
                        if (!_disposed)
                        {
                            if (Size != _prevSize)
                            {
                                if (Size >= 0)
                                {
                                    if (_pointer != IntPtr.Zero)
                                    {
                                        Marshal.FreeHGlobal(_pointer);
                                        _pointer = IntPtr.Zero;
                                    }
                                    if (Size > 0)
                                    {
                                        _pointer = Marshal.AllocHGlobal((int)Size);
                                    }
                                }
                                else
                                {
                                    throw new ArgumentOutOfRangeException("Size can not be 0 or less.");
                                }
                                _prevSize = Size;
                            }
                        }
                        else
                        {
                            throw new ObjectDisposedException(null);
                        }
                        return _pointer;
                    }
                }
            }

            public EntryInfo EntryInfo
            {
                get
                {
                    lock (this)
                    {
                        if (_pointer != IntPtr.Zero)
                        {
                            return (EntryInfo)Marshal.PtrToStructure(Pointer, typeof(EntryInfo));
                        }
                        else
                        {
                            return new EntryInfo();
                        }
                    }
                }
            }

            #region IDisposable Members

            public void Dispose()
            {
                lock (this)
                {
                    if ((_disposed == false) & (_pointer != IntPtr.Zero))
                    {
                        Marshal.FreeHGlobal(_pointer);
                        _pointer = IntPtr.Zero;
                        _disposed = true;
                    }
                }
            }

            ~EntryInfoWrapper()
            {
                Dispose();
            }

            #endregion
        }

        [DllImport("wininet.dll", SetLastError = true)]
        private static extern long DeleteUrlCacheEntry(string lpszUrlName);

        [DllImport("wininet.dll", SetLastError = true)]
        private static extern long FindCloseUrlCache(IntPtr hEnumHandle);

        [DllImport("wininet.dll", SetLastError = true)]
        private static extern IntPtr FindFirstUrlCacheEntry(string lpszUrlSearchPattern, IntPtr lpFirstCacheEntryInfo, out UInt32 lpdwFirstCacheEntryInfoBufferSize);

        [DllImport("wininet.dll", SetLastError = true)]
        private static extern long FindNextUrlCacheEntry(IntPtr hEnumHandle, IntPtr lpNextCacheEntryInfo, out UInt32 lpdwNextCacheEntryInfoBufferSize);


        public static void DeleteInternetCacheEntries(string urlPattern)
        {
            IntPtr hEnum = IntPtr.Zero;

            try
            {
                using (EntryInfoWrapper buffer = new EntryInfoWrapper())
                {
                    FindFirstUrlCacheEntry(null, buffer.Pointer, out buffer.Size);
                    if ((hEnum = FindFirstUrlCacheEntry(urlPattern, buffer.Pointer, out buffer.Size)) == IntPtr.Zero)
                    {
                        throw new Exception(((Error)Marshal.GetLastWin32Error()) + " Url pattern: " + urlPattern);
                    }
                    if (buffer.EntryInfo.lpszSourceUrlName.Contains(urlPattern))
                    {
                        if (DeleteUrlCacheEntry(buffer.EntryInfo.lpszSourceUrlName) == 0)
                        {
                            throw new Exception(((Error)Marshal.GetLastWin32Error()) + " Url: " + urlPattern);
                        }
                        System.Diagnostics.Debug.WriteLine(buffer.EntryInfo.lpszSourceUrlName);
                    }
                }

                while (true)
                {


                    using (EntryInfoWrapper buffer = new EntryInfoWrapper())
                    {
                        FindNextUrlCacheEntry(hEnum, buffer.Pointer, out buffer.Size);
                        if ((Error)Marshal.GetLastWin32Error() == Error.NoMoreItems)
                        {
                            break;
                        }
                        FindNextUrlCacheEntry(hEnum, buffer.Pointer, out buffer.Size);
                        if (buffer.EntryInfo.lpszSourceUrlName.Contains(urlPattern))
                        {
                            if (DeleteUrlCacheEntry(buffer.EntryInfo.lpszSourceUrlName) == 0)
                            {
                                throw new Exception(((Error)Marshal.GetLastWin32Error()) + " Url: " + urlPattern);
                            }
                            System.Diagnostics.Debug.WriteLine(buffer.EntryInfo.lpszSourceUrlName);
                        }
                    }
                }
            }
            finally
            {
                if (hEnum != IntPtr.Zero)
                {
                    FindCloseUrlCache(hEnum);
                }
            }
        }
    }
}
